//
// Created by LiuYou on 2021/10/25.
//

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <csignal>
#include <sys/time.h>


static int srcFd;

static volatile bool isLoop{false};


static void releaseResources() {
    close( srcFd );

    const char str[] = "释放文件描述符。\n.";
    write( STDOUT_FILENO, str, sizeof( str ) - 1 );
}


static void alarmHandler(int signal) {
    isLoop = true;
}


int main(int argc, char** argv) {
//    signal( SIGALRM, alarmHandler );
//    signal( SIGINT, alarmHandler );
//    signal( SIGTERM, alarmHandler );


    // todo 笔记: 有几个信号如果使用的是同一个信号处理函数, 那么就用一个这样的模块代替
    //  一个 signal(); 函数。
//    struct sigaction sa{};
//    sa.sa_handler = alarmHandler;
//    // sigaction的第二个参数是一个信号集。sigset_t
//    // 所以关心信号集的函数就可以使用了。
//    sigemptyset( &sa.sa_mask );
//    // 我希望响应 SIGALRM信号的时候, 其它信号 BLOCK 住。
//    // todo 笔记: 注意: 当在相应这个信号的时候, 将其它信号阻塞住, 这样就有效的防止了重入现象。
//    //  所以, 这也是推荐使用 sigaction(); 而不是 signal(); 的原因。
//    sigaddset( &sa.sa_mask, SIGQUIT );
//    sigaddset( &sa.sa_mask, SIGTERM );
//    // sa_flags: 字段表示特殊要求。没有就就置为 0。
//    sa.sa_flags = 0;
//    sigaction( SIGALRM, &sa, nullptr );

    // todo 笔记: 但是要写好多重复的代码, 所以使用下面这样的写法。
    struct sigaction sa{};
    sa.sa_handler = alarmHandler;
    // sigaction的第二个参数是一个信号集。sigset_t
    // 所以关心信号集的函数就可以使用了。
    sigemptyset( &sa.sa_mask );
    // 我希望响应 SIGALRM信号的时候, 其它信号 BLOCK 住。
    // todo 笔记: 注意: 当在相应这个信号的时候, 将其它信号阻塞住, 这样就有效的防止了重入现象。
    //  所以, 这也是推荐使用 sigaction(); 而不是 signal(); 的原因。
    sigaddset( &sa.sa_mask, SIGQUIT );
    sigaddset( &sa.sa_mask, SIGTERM );
    sigaddset( &sa.sa_mask, SIGALRM );
    // sa_flags: 字段表示特殊要求。没有就就置为 0。
    sa.sa_flags = 0;

    sigaction( SIGALRM, &sa, nullptr );
    sigaction( SIGQUIT, &sa, nullptr );
    sigaction( SIGTERM, &sa, nullptr );


    itimerval itv{};
    itv.it_interval.tv_sec = 1;
    itv.it_interval.tv_usec = 0;
    itv.it_value.tv_sec = 1;
    itv.it_value.tv_usec = 0;
    // todo 注意: 将第三个参数设置为空即表示不关心之前的时钟计时。
    if (setitimer( ITIMER_REAL, &itv, nullptr ) < 0) {
        perror( "setitimer()" );
        exit( 1 );
    }


    srcFd = open( argv[1], O_RDONLY );
    atexit( releaseResources );


    char buffer[100]{};
    ssize_t readBytes{};
    while (true) {

        while (!isLoop) {
            pause();
        }
        isLoop = false;


        readBytes = read( srcFd, buffer, sizeof( buffer ));
        if (readBytes < 0) {
            perror( "read()" );
            exit( 1 );
        }
        if (readBytes == 0) {
            break;
        }

        ssize_t writeBytes{};
        int writePos{};
        while ((writeBytes = write( STDOUT_FILENO, buffer + writePos, readBytes )) != readBytes) {
            writePos += static_cast<int>(writeBytes);
            readBytes -= writePos;
        }

    }


    return 0;
}
